import os
import pandas as pd
from mailmerge import MailMerge
from docx import Document,shared

from docx.oxml.ns import qn
from docx.enum.text import WD_ALIGN_PARAGRAPH #设置对象居中、对齐等。
from docx.enum.text import WD_TAB_ALIGNMENT,WD_TAB_LEADER #设置制表符等
from docx.enum.text import WD_UNDERLINE
from docx.enum.text import WD_TAB_ALIGNMENT
from docx.enum.table import WD_ALIGN_VERTICAL
from docx.enum.table import WD_ROW_HEIGHT_RULE
from docx.enum.text import WD_LINE_SPACING
from docx.shared import Inches #设置图像大小
from docx.shared import Pt #设置像素、缩进等
from docx.shared import RGBColor #设置字体颜色
from docx.shared import Length #设置宽度
import time
import datetime
# 自定义包
import config
import utils

class Statement(object):
    '''
    description: 初始化对象
    param {*} self
    param {str} code: 来自初始化时输入 
    param {str} name: 产品名称 从self.data中读取 
    param {str} ingrident: 产品成分，来自读取本地的.\data\product_info.csv文件，从Lark多维表格同步
    param {dataframe} template: Statement的项目以及每一项的内容模板，来自读取本地.\template\statement template.csv 文件
    param {dataframe} data: 需要书写的statement,来自读取本地.\data\statement.csv 文件,从Lark 多维表格同步
    param {list} error: 错误列表，记录程序运行中的错误
    return {*}
    '''    
    def __init__(self,code):
        self.code = code
        self.template = self.statement_template()
        self.data = self.statement_need()
        self.name = self.data.iloc[0,1]
        self.ingrident = self.get_ingrident()
        self.error=[]
    #
    # 读取位于指定路径下的CSV文件，并根据指定的列名选择特定的列，最后返回处理后的DataFrame对象。
    def statement_template(self):
        # 读取位于config.TEMPLATE_PATH目录下的statement template.csv文件，并将其存储为DataFrame对象df
        df = pd.read_csv(os.path.join(config.TEMPLATE_PATH,"statement template.csv"))
        # 将df的索引设置为"Key"列的值
        df.index = df["Key"] 
        # 选择df中"Heading":"Statement"之间的所有列，并将结果存储回df
        df = df.loc[:, "Heading":"Statement"]
        # 返回处理后的df
        return df
    # 读取位于指定路径下的CSV文件，并根据指定的列名选择特定的列，最后返回处理后的DataFrame对象。
    
    def statement_need(self):
        # 读取位于config.DATA_PATH目录下的statement.csv文件，并将其存储为DataFrame对象df
        df = pd.read_csv(os.path.join(config.DATA_PATH,"statement.csv"))
        # 根据传入的产品代码筛选出相应的行，并将结果存储回df
        df = df[df['product code']==self.code]
        # 返回处理后的df
        return df
    
    # get_ingrident: 根据传入的产品代码，从指定的CSV文件中筛选出相应的行，并从中获取指定列的值，最后返回该值。
    def get_ingrident(self):
        # 读取位于config.DATA_PATH目录下的product_info.csv文件，并将其存储为DataFrame对象df
        df = pd.read_csv(os.path.join(config.DATA_PATH,"product_info.csv"))
        # 根据传入的产品代码筛选出相应的行，并将结果存储回df
        df = df[df['Product Code']==self.code]
        # 从df中选择"Item"列等于"Composition"且"Target"不为空的行，并获取其"Target"列的值，将结果存储在ingrident变量中
        ingrident = df.loc[df['Item']=='Composition','Target'].values[0]
        # 返回ingrident变量的值
        return ingrident

    
    '''
    这段代码定义了一个名为`statement_mailmerge`的函数，用于生成产品声明的邮件合并文档。下面是对代码中各部分的注释说明：

    - `document_type`：设置文档类型为"Product Statement"，表示生成的产品声明文档。
    - `template`：获取模板文件的路径，模板文件名为"BKD - Statement template.docx"，位于配置项`config.TEMPLATE_PATH`指定的路径下。
    - `document`：创建一个邮件合并对象，并将模板文件加载到该对象中。
    - `document.merge()`：调用邮件合并对象的`merge()`方法，将邮件合并的内容与指定参数进行合并。合并的内容包括产品名称、产品代码、文档类型、当前月份和年份以及成分信息。
    - `fname`：构建保存文件的文件名，格式为"BKD - 产品代码 文档类型.docx"。
    - `save_path`：构建保存文件的路径，由配置项`config.SAVE_PATH`和产品名称及代码组成。
    - `os.makedirs()`：如果保存路径不存在，则创建该路径。
    - `filename`：构建保存文件的完整路径和文件名，由保存路径、文件名组成。
    - `document.write()`：将合并后的文档写入保存文件。
    - `return filename`：返回保存文件的完整路径和文件名。  
    '''
    def statement_mailmerge(self):
        # 设置文档类型为产品声明
        document_type = 'Product Statement'
        
        # 获取模板文件路径
        template = os.path.join(config.TEMPLATE_PATH, "BKD - Statement template.docx")
        
        # 创建邮件合并对象，并加载模板文件
        document = MailMerge(template)
        
        # 将邮件合并内容与指定参数进行合并
        document.merge(ProductName=self.name,
                    ProductCode=self.code,
                    DocumentType=document_type,
                    MonthYear=time.strftime("%b %Y", time.localtime()),
                    Ingredient=self.ingrident
                    )
        
        # 构建保存文件的文件名
        fname = 'BKD - {} {}.docx'.format(self.code, document_type)
        
        # 构建保存文件的路径
        save_path = os.path.join(config.SAVE_PATH, self.code + " " + self.name)
        
        # 如果保存路径不存在，则创建该路径
        if not os.path.exists(save_path):
            os.makedirs(save_path)
        
        # 构建保存文件的完整路径和文件名
        filename = os.path.join(save_path, fname)
        
        # 将合并后的文档写入保存文件
        document.write(filename)
        
        # 返回保存文件的完整路径和文件名
        return filename

    '''
    description: 写statement文件
    param {*} self
    return {str} filename: 生成的statement文件名
    '''
    def write_statements(self):
        
        # statement_template = os.path.join(config.TEMPLATE_PATH,"BKD - Statement template.docx") #旧版程序遗留
        # 生成改好文件头的模板
        filename=self.statement_mailmerge()
        doc=Document(filename)
        
        df = self.data.T # 转置statement数据
        
        # 单独判断Plant Source的情况，修改Organic和Nature两个声明的表达
        plant_source = df.loc['Plant Source'].iloc[0]
        if type(plant_source)==str:
            self.template.loc['Organic','Statement'] = self.template.loc['Organic','Statement'].replace('0000',plant_source)
            self.template.loc['Natural','Statement'] = self.template.loc['Natural','Statement'].replace('0000',plant_source)
        else:    
            self.error.append("Plant Source is null")
        # 筛选出需要生成和不需要生成的数据
        false_statement = list(df[df.iloc[:,0]==False].index) # self.data中不需要写的statement清单
        self.error.append("False statement:{}".format(false_statement))# 记录到self.error列表中
        
        true_statement = list(df[df.iloc[:,0]==True].index) # self.data 中需要写的statement清单
        missing_statement = [i for i in true_statement+false_statement if i not in self.template.index ] # statement template中缺少的项目
        if missing_statement !=[]: self.error.append("Missing Statement in template:{}".format(missing_statement)) # 记录到self.error 列表中
        
        
        writen_statement = [i for i in self.template.index if i in true_statement] # 准备需要写入的statement 清单
        df = self.template.loc[writen_statement] # 将需要书写的Statement清单作为mask，生成包含书写内容的dataframe
        
        for i in list(df.index):
            stat_heading = doc.add_heading("",1)
            stat_heading.paragraph_format.space_after = Pt(5)
            stat_heading.paragraph_format.space_before = Pt(5)
            stat_heading.paragraph_format.line_spacing = Pt(0)
            try:
                stat_heading_run = stat_heading.add_run(df.loc[i,"Heading"])
                stat_heading_run.font.color.rgb = RGBColor(0,0,0)
                stat_par1 = doc.add_paragraph()
                statement = str(df.loc[i,"Statement"]).replace("xxxx",self.name)
                stat_par1_run1 = stat_par1.add_run(statement)
            except Exception as e:
                print (e)
        
        # 写末尾的总结声明
        stat_par1 = doc.add_paragraph()
        end_statement = str("This document is valid for all the batches for the mentioned product supplied to all customers.")
        stat_par1.add_run(end_statement)
        doc.save(filename)
        print(f'{self.code} statement generate complete')
        return filename


if __name__ == "__main__":
    #df = pd.read_csv(os.path.join("D:\RobinCode\generate-product-documents\data\statement.csv"))
    code = 'SR0297'
    #df[df['product code']==code]
    stat1 = Statement(code)